import java.util.HashMap;
import java.util.Collection;
import java.util.GregorianCalendar;
import java.io.Serializable;
import java.io.ObjectOutputStream;
import java.io.ObjectInputStream;
import java.io.FileOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Calendar;

/**Class represents a managing-system for courses and users at university
 * 
 * @author Kurt Micheli
 * @author Gabriel Rauter
 * @author Max Mulatz
 */
public class CourseManager implements Serializable{
	
	private HashMap<String, Course> courses = null;
	private HashMap<String,Person> users = null;
	
	/**Creats an CourseManager.
	*/
	public CourseManager() {
		courses = new HashMap<String, Course>();
		users = new HashMap<String, Person>();
	}
	
	public void addUser(String id, String name, String surname) {
		if(!this.users.containsKey(id)) {
			if(id.startsWith("s")) {
				Person temp = new Student(id, name, surname);
				users.put(id, temp);
			}else if(id.startsWith("p")) {
				Person temp = new Professor(id, name, surname);
				users.put(id, temp);
			}
		}
	}
	
	public void makeTutor(String matnr, String id) {
		if(!this.users.containsKey(matnr)) {
			if(this.users.containsKey(matnr)) {
				if(this.users.get(matnr) instanceof Student) {
					Student temps = (Student) users.get(matnr);
					Tutor tempt = temps.getTutor(id);
					users.put(tempt.getId(),tempt);
				}
			}
		}
	}
	
	public void giveCertificate(String id, String course) {
	}
		
	/**Adds a new course to the overview of all courses available at university.
	 * 
	 * @param lvanr The numeric description of the course.
	 * @param title The name of the course.
	 * @param enrollStart   The day from which on Students can enroll into the course
	 * @param enrollEnd The day when the period for Students to enroll into the course ends.
	 * @param checkOutEnd   The last possible day for Students to leave the course. 
	 */
	public void addCourse(String type, String lvanr, String title, double ects, double workinghours, GregorianCalendar enrollStart, GregorianCalendar enrollEnd, GregorianCalendar checkOutEnd, String id, boolean visible) {
		if(!this.courses.containsKey(lvanr) && this.users.containsKey(id)) {
			if(users.get(id) instanceof Professor) {
				Course temp = null;
				if (type.equals("VO")) {
					temp = new VO(lvanr, title,ects, workinghours, enrollStart, enrollEnd, checkOutEnd, (Professor)this.users.get(id), visible); 
				} else if (type.equals("VU")) {
					temp = new VU(lvanr, title,ects, workinghours, enrollStart, enrollEnd, checkOutEnd, (Professor)this.users.get(id), visible); 
				} else if (type.equals("UE")) {
					temp = new UE(lvanr, title,ects, workinghours, enrollStart, enrollEnd, checkOutEnd, (Professor)this.users.get(id), visible); 
				}
				courses.put(temp.getLvanr(), temp);
			}
		}
	}
	
	public void addCourse(String type, String lvanr, String title, double ects, double workinghours, GregorianCalendar enrollStart, GregorianCalendar enrollEnd, GregorianCalendar checkOutEnd, String id) {
		if(!this.courses.containsKey(lvanr) && this.users.containsKey(id)) {
			if(this.users.get(id) instanceof Professor) {
				Course temp = null;
				if (type.equals("VO")) {
					temp = new VO(lvanr, title,ects, workinghours, enrollStart, enrollEnd, checkOutEnd, (Professor)this.users.get(id));  
				} else if (type.equals("VU")) {
					temp = new VU(lvanr, title,ects, workinghours, enrollStart, enrollEnd, checkOutEnd, (Professor)this.users.get(id));  
				} else if (type.equals("UE")) {
					temp = new UE(lvanr, title,ects, workinghours, enrollStart, enrollEnd, checkOutEnd, (Professor)this.users.get(id)); 
				}
				courses.put(temp.getLvanr(), temp);
			}
		}
	}
	
	public void addExam(String course, int maxPoints, GregorianCalendar date) {
		if(this.courses.containsKey(course)){
			if(this.courses.get(course) instanceof VO) {
				VO temp = (VO)courses.get(course);
				temp.getFinalExams().addExam(courses.get(course), maxPoints, date);
			}
		}
	}
	
	public void enrollExam(String course, String id, FinalExam exam) {
		if(this.courses.containsKey(course) && this.users.containsKey(id)){
			if(this.courses.get(course) instanceof VO && this.users.get(id) instanceof Student) {
				VO temp = (VO)courses.get(course);
				temp.getFinalExams().enrollExam((Student) this.users.get(id), exam);
			}
		}
	}
	
	public void addAbsolvedExam(String course , String id, FinalExam exam, int achievedPoints, int mark) {
		if(this.courses.containsKey(course) && this.users.containsKey(id)){
			if(this.courses.get(course) instanceof VO && this.users.get(id) instanceof Student) {
				VO temp = (VO)courses.get(course);
				temp.getFinalExams().addAbsolvedExam((Student) this.users.get(id), exam, achievedPoints, mark);
			}
		}
	}
	
	public void cancelCourse(String course) {
		if(courses.containsKey(course)){
			courses.get(course).cancel();
		}
	}
	
	public void addRequirements(String tocourse, String course) {
		if(courses.containsKey(tocourse)) {
				courses.get(tocourse).addRequirements(courses.get(course));
		}
	}
				
    /**Enrolls a special Student into the specified course if the course is really available at university. Method uses course's method to enroll Students.
	 * 
	 * @param course The specified course into which a student should be enrolled.
	 * @param student   The Student that wants to enroll into the specified course.
	 * @return  True if the Student was sucessfully enrolled, false otherwise. 
	 */
	public boolean enroll(String course ,Student student) {
		if(courses.containsKey(course)) {
			return courses.get(course).enroll(student);
		} else {
			return false;
		}
	}
	
	/**Checks out a Student of the speciefied course, if the course really exists at university. Method uses the course's method to check out Studetns.
	 * 
	 * @param course The course from which the Student should be checked out.
	 * @param student The Student that wants to be checked out of the specified course.
	 * @return True if the Student was sucessfully checked out from the course, false otherwise.
	 */
	public boolean checkOut(String course ,Student student) {
		if(courses.containsKey(course)) {
			return courses.get(course).checkOut(student);
		} else {
			return false;
		}
	}

	public void save(){
		GregorianCalendar cal = new GregorianCalendar();
		String out = "backup_"+cal.get(Calendar.DAY_OF_MONTH)+"_"+cal.get(Calendar.MONTH)+"_"+cal.get(Calendar.YEAR)+"_"+cal.get(Calendar.HOUR_OF_DAY)+"-"+cal.get(Calendar.MINUTE)+":"+cal.get(Calendar.SECOND) +".cm";
		try{
			FileOutputStream fos = new FileOutputStream("backup/"+out);
			ObjectOutputStream oos = new ObjectOutputStream(fos);
			oos.writeObject(this);
			oos.flush();
			oos.close();
		}catch(IOException e){
			System.out.println(e.getMessage());
		}	
	}
	
	public void load(String in){
		try{
			FileInputStream fis = new FileInputStream("backup/"+in);
			ObjectInputStream ois = new ObjectInputStream(fis);
			CourseManager temp = null;
			try{
				temp = (CourseManager) ois.readObject();
			}catch(ClassNotFoundException e){
				System.out.println(e.getMessage());
			}
			
			ois.close();
		}catch(IOException e){
			System.out.println(e.getMessage());
		}
			
	}
	
	public Course getCourse(String course) {
		if(courses.containsKey(course)){
			return courses.get(course);
		}else {
			return null;
		}
	}
	
	public Person getPerson (String id) {
		if(users.containsKey(id)){
			return users.get(id);
		}else {
			return null;
		}
	}
	
    /**Returns an overview of all Students currently enrolled into the course, if the course is really available at university. The method uses the course's overview of Students. 
     * 
     * @param course The special course from which the detailled information is questioned.
     * @return A list of Strings, every line containing detailled information about one of the Students currently enrolled into the course.
     */
	public String getStudents(String course) {
		if(courses.containsKey(course)) {
			Student temp[] = new Student[courses.get(course).getStudents().size()];
			Collection<Student> a = courses.get(course).getStudents().values();
			temp = a.toArray(temp);
			String out ="";
			for(int i = 0;i < temp.length;i++) {
				out = out + temp[i]+'\n';
			}
			return out;
		} else {
			return "No Course Found";
		}
		
	}
		
	/**Returns a list of all courses currently available at university and their basic information.
	 * 
	 * @return A list of Strings, every line containing the basic information about one of the courses currently available at university.
	 */
	public String toString() {
		Course temp[] = new Course[courses.size()];
		Collection<Course> a = courses.values();
		temp = a.toArray(temp);
		String out ="";
		for(int i = 0;i < temp.length;i++) {
			if(temp[i].getVisible()) {
				out = out + temp[i]+'\n';
			}
		}
		return out;
	}
}